/*
 * Author:
 * 		Davi Costa Clemente 		RA 083413
 * 		Fernando Lener de Arruda	RA 081391
 */

#ifndef COMMON_H_
#define COMMON_H_
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>


#define ALLOC(type, n)\
	(type *)malloc(sizeof(type)*(n))

#define BIBLIO_FILENAME "livros.txt"

#define MAX_REQUESTS 100

//1MB buffer size
#define BUFFER_SIZE 1<<20

/*
 *	Formato da mensagens de request
 *	4 bytes = tamanho da mensagem
 *	4 bytes = tipo do cliente
 *	4 bytes = tipo da request
 *	opcional 4 bytes = tamanho do ISBN
 *	opcional variavel = ISBN
 *	opcional 4 bytes = valor a ser setado no estoque
 *
 *
 *	Formato da mensagem de resposta
 *	4 bytes = tamanho da mensagem
 *	4 bytes = tempo de processamento do servidor
 *	4 bytes = tipo do erro
 *	4 bytes = tamanho da mensagem de resposta
 *	variavel = dados de resposta
 */

typedef struct {
	char titulo[100];
	char autor[100];
	char editora[100];
	char descricao[3000];
	int anoPublicacao;
	char ISBN10[12];
	int exemplares;
} Livro;

typedef struct {
	int numLivros;
	Livro* livros;
} Biblioteca;

typedef enum {
	ALL_ISBNAndTitle,
	SINGLE_GetDescription,
	SINGLE_GetFullInfo,
	ALL_GetFullInfo,
	SINGLE_UpdateStock,
	SINGLE_GetSampleCount,
	CloseConnection
} RequestType;

typedef struct {
	int totalTime;
	int totalRequests;
	int totalFailures;
	int each[MAX_REQUESTS];
} SingleStatistics;

typedef struct {
	//CloseConnection deve ser nosso ultimo enum sempre
	SingleStatistics times[CloseConnection];
} Statistics;

typedef enum {
	None, InvalidUser, InvalidRequest, Unknown
} ErrorType;

typedef enum {
	User, Bookstore
} ClientType;

typedef struct {
	char* error;

} Reply;

//Adiciona uma string (precedida de seu tamanho)
//no começo do buffer e retorna um ponteiro
//da ultima posição de onde terminou a concatenação
//[tamanho em 4 bytes|resto da string]
char * addString(char * buffer, char *str);

//Aloca uma string lida de um buffer no formato:
//tamanho string + string e retorna a posição de onde a leitura terminou
char * readString(char *buffer, char **str);

//Adiciona um inteiro(binário) começo do buffer e retorna um ponteiro
//da ultima posição de onde terminou a concatenação
char * addInt(char * buffer, int n);

//Lê um inteiro(binário) do buffer e retorna a posição
//onde a leitura terminou
char * readInt(char *buffer, int* n);

//Manda uma mensagem por completo
//Retorna 0 se mandou com sucesso, 1 se falhou
int safeSend(char * msg, int socket,struct sockaddr_in* in_addr);

//Recebe uma mensagem inteira de um dos lados
int safeReceive(char **msg, int socket,struct sockaddr_in* out_addr);

//Funções de cliente

//Associa um socket UDP à um endereço e porta do servidor
int pseudoUDPConnect(char * address, unsigned short port,struct sockaddr_in* out_addr);

//Faz uma request para o servidor
//Se optionalIBSN não for necessário, ele deve receber NULL
char * createBookRequest(RequestType type, ClientType client, char *optinalISBN,
		int optionalUpdateStockSize);

//Funções de servidor

//Cria um socket que será utilizado para receber mensagens na porta
//especificada
//Retorna -1 se falhar
int createUDPSocket(unsigned short port);


//Bloqueia esperando ler uma request do cliente e devolve os dados
//nos parametros começados com out_
//optionalISBN deve ser uma string pré-alocada de tamanho minimo==11
int receiveBookRequest(ClientType* out_ctype, RequestType* out_rtype,
		char **out_optionalISBN, int* out_optionalUpdateStockSize, int socket,struct sockaddr_in* in_addr);


//Carrega todos os livros na biblioteca
void loadBooks(char *filename, Biblioteca* biblio);

//Escreve todos os livros no arquivo especificado
void writeBooks(char *filename, Biblioteca* biblio);

//Trata uma request do cliente e retorna
//A mensagem de resposta
char * createBookRequestReply(ClientType ctype, RequestType rtype,
		char *optionalISBN, int optionalUpdateStockSize);



#endif /* COMMON_H_ */
